<template>
  <div id="coverCot" style="width: 100vw; height: 100vh;overflow: hidden">
    <section class="section-cot" style="width: 100%; height: 100%;">
      <sider-dnd id="stencil" ref="stencil"></sider-dnd>
      <div id="container" @click.stop="hideFn">

        <MenuBar v-if="showContextMenu" ref="menuBar" @callBack="contextMenuFn" />
        <header>
          <el-tooltip class="item" effect="dark" content="项目" placement="bottom">
            <i class="el-icon-menu" @click="showDrawerFn()" />
          </el-tooltip>
          <el-tooltip class="item" effect="dark" content="长按shift多选" placement="bottom">
            <i class="el-icon-crop" />
          </el-tooltip>
          <el-tooltip class="item" effect="dark" content="放大" placement="bottom">
            <i class="el-icon-zoom-in" @click="zoomFn(0.2)" />
          </el-tooltip>
          <el-tooltip class="item" effect="dark" content="缩小" placement="bottom">
            <i class="el-icon-zoom-out" @click="zoomFn(-0.2)" />
          </el-tooltip>
          <el-tooltip class="item" effect="dark" content="适应屏幕" placement="bottom">
            <i class="el-icon-full-screen" @click="centerFn" />
          </el-tooltip>
          <el-tooltip class="item" effect="dark" content="执行" placement="bottom">
            <i class="el-icon-video-play" @click="startFn()" />
          </el-tooltip>
          <el-tooltip class="item" effect="dark" content="保存" placement="bottom">
            <i class="el-icon-upload" @click="saveFn()" />
          </el-tooltip>
          <el-tooltip class="item" effect="dark" content="加载保存页面" placement="bottom">
            <i class="el-icon-link" @click="loadFn()" />
          </el-tooltip>
          <el-tooltip class="item" effect="dark" content="是否禁用" placement="bottom">
            <i :class="{ 'el-icon-lock': isLock, 'el-icon-unlock': !isLock }" @click="lockFn()" />
          </el-tooltip>
          <!-- <el-button class="setTitle" type="primary" @click="dialogTitleVisible=true">设置标题</el-button> -->
        </header>
        <div id="draw-cot" />
      </div>
    </section>
    <user-task ref="userTask"></user-task>
    <sequence-flow ref="sequenceFlow"></sequence-flow>
    <DialogCondition ref="dialogCondition"></DialogCondition>
    <DialogMysql ref="dialogMysql"></DialogMysql>
    <el-dialog title="设置标题" :visible.sync="dialogTitleVisible">
      <el-form>
        <el-form-item label="标题名称" label-width="120px">
          <el-input v-model="title" autocomplete="off"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogTitleVisible = false">确 定</el-button>
      </div>
    </el-dialog>

  </div>
</template>

<script>
import { Graph, Node, Path, Cell, Shape, Addon } from '@antv/x6'
import '@antv/x6-vue-shape'
import { saveProcess, updateProcess, listProcess, getProcessInfo, deleteProcess } from "@/api/activiti/customprocess";
import database from './nodeTheme/database.vue'
import condition from './nodeTheme/condition.vue'
import onlyout from './nodeTheme/onlyOut.vue'
import onlyin from './nodeTheme/onlyIn.vue'
import UserTask from './dialog/userTask.vue'
import SequenceFlow from './dialog/sequenceFlow.vue'
import Stencil from './stencil.vue'
import siderDnd from './sider-dnd.vue'
import DataJson from './data'
import MenuBar from './menuBar'
import Drawer from './drawer'
import DialogCondition from './dialog/condition.vue'
import DialogMysql from './dialog/mysql.vue'
import { uuid } from '@antv/x6/lib/util/string/uuid';

export default {
  name: 'AppProcess',
  components: { MenuBar, Drawer, DialogCondition, DialogMysql, UserTask, SequenceFlow, Stencil, siderDnd },
  data() {
    return {
      id: '',
      title: '',
      drawer: false,
      graph: '',
      timer: '',
      isLock: false,
      dialogTitleVisible: false,
      showUserTask: false,
      graphData: '',
      showContextMenu: false
    }
  },
  async mounted() {
    // 初始化X6引擎流程图
    this.initGraph()
    // 给流程图绑定按键监听
    this.keyBindFn()
    // 调用后台接口获取已保存流程图信息。
    await this.getProcessById()
    // 加载显示流程图信息
    this.startFn()
  },
  methods: {
    async getProcessById() {
      this.id = this.$route.query.modelId;
      let response = await getProcessInfo(this.id);
      console.log('response ', response)
      this.graphData = response.data.graphData;
    },
    getNodeById(id) {
      return this.graph.getCellById(id)
    },
    hideFn() {
      this.showContextMenu = false
    },
    initGraph() {
      const time = new Date().getTime()

      // 注册节点
      Graph.registerNode(
        'dag-condition',
        {
          inherit: 'vue-shape',
          width: 180,
          height: 36,
          component: {
            template: `<condition />`,
            components: {
              condition
            }
          },
          ports: {
            groups: {
              top: {
                position: 'top',
                attrs:
                {
                  circle: {
                    r: 4,
                    magnet: true,
                    stroke:
                      '#C2C8D5',
                    strokeWidth: 1,
                    fill: '#fff'
                  }
                }
              },
              bottom: {
                position: 'bottom',
                attrs: {
                  circle: {
                    r: 4,
                    magnet:
                      true,
                    stroke: '#C2C8D5',
                    strokeWidth: 1,
                    fill: '#fff'
                  }
                }
              }
            }
          }
        },
        true
      )

      Graph.registerNode(
        'start-event',
        {
          inherit: 'vue-shape',
          width: 180,
          height: 36,
          component: {
            template: `<onlyout />`,
            components: {
              onlyout
            }
          },
        },
        true
      )

      Graph.registerNode(
        'end-event',
        {
          inherit: 'vue-shape',
          width: 180,
          height: 36,
          component: {
            template: `<onlyout />`,
            components: {
              onlyout
            }
          },
        },
        true
      )

      Graph.registerNode(
        'user-task',
        {
          inherit: 'vue-shape',
          width: 180,
          height: 36,
          component: {
            template: `<onlyin />`,
            components: {
              onlyin
            }
          },
          ports: {
            groups: {
              top: {
                position: 'top',
                attrs:
                {
                  circle: {
                    r: 4,
                    magnet: true,
                    stroke:
                      '#C2C8D5',
                    strokeWidth: 1,
                    style: {
                      visibility: 'hidden',
                    },
                    fill: '#fff'
                  }
                }
              },
              left: {
                position: 'left',
                attrs: {
                  circle: {
                    r: 4,
                    magnet:
                      true,
                    stroke: '#C2C8D5',
                    strokeWidth: 1,
                    style: {
                      visibility: 'hidden',
                    },
                    fill: '#fff'
                  }
                }
              },
              bottom: {
                position: 'bottom',
                attrs: {
                  circle: {
                    r: 4,
                    magnet:
                      true,
                    stroke: '#C2C8D5',
                    strokeWidth: 1,
                    style: {
                      visibility: 'hidden',
                    },
                    fill: '#fff'
                  }
                }
              },
              right: {
                position: 'right',
                attrs: {
                  circle: {
                    r: 4,
                    magnet:
                      true,
                    stroke: '#C2C8D5',
                    strokeWidth: 1,
                    style: {
                      visibility: 'hidden',
                    },
                    fill: '#fff'
                  }
                }
              },
            },
            items: [
              {
                group: 'top' // 指定分组名称
              },
              {
                group: 'left' // 指定分组名称
              },
              {
                group: 'bottom' // 指定分组名称
              },
              {
                group: 'right'
              }
            ]
          },
        },
        true
      )




      Graph.registerNode(
        'dag-node',
        {
          inherit: 'vue-shape',
          width: 180,
          height: 36,
          component: {
            template: `<database />`,
            components: {
              database
            }
          },
          ports: {
            groups: {
              top: {
                position: 'top',
                attrs:
                {
                  circle: {
                    r: 4,
                    magnet: true,
                    stroke:
                      '#C2C8D5',
                    strokeWidth: 1,
                    fill: '#fff'
                  }
                }
              },
              bottom: {
                position: 'bottom',
                attrs: {
                  circle: {
                    r: 4,
                    magnet:
                      true,
                    stroke: '#C2C8D5',
                    strokeWidth: 1,
                    fill: '#fff'
                  }
                }
              }
            }
          }
        },
        true
      )

      Graph.registerEdge(
        'dag-edge',
        {
          inherit: 'edge',
          data: {
            bpmnType: 'sequenceFlow'
          },
          router: {
            name: 'orth',
          },
          attrs: {
            line: {
              stroke: '#C2C8D5',
              strokeWidth: 2,
              targetMarker: {
                name: 'block',
                width: 12,
                height: 8
              }
            }
          }
        },
        true
      )
      Graph.registerConnector(
        'algo-connector',
        (s, e) => {
          const offset = 4
          const deltaY = Math.abs(e.y - s.y)
          const control = Math.floor((deltaY / 3) * 2)

          const v1 = { x: s.x, y: s.y + offset + control }
          const v2 = { x: e.x, y: e.y - offset - control }

          return Path.normalize(
            `M ${s.x} ${s.y}
           L ${s.x} ${s.y + offset}
           C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}
           L ${e.x} ${e.y}
          `
          )
        },
        true
      )
      const graph = new Graph({
        resizing: true,
        grid: {
          size: 10,
          visible: true,
          type: 'dot', // 'dot' | 'fixedDot' | 'mesh'
          args: {
            color: '#a05410', // 网格线/点颜色
            thickness: 1 // 网格线宽度/网格点大小
          }
        },
        background: {
          color: '#fffbe6' // 设置画布背景颜色
        },
        container: document.getElementById('draw-cot'),
        panning: {
          enabled: true,
          eventTypes: ['leftMouseDown', 'mouseWheel']
        },
        mousewheel: {
          enabled: true,
          modifiers: 'ctrl',
          factor: 1.1,
          maxScale: 1.5,
          minScale: 0.5
        },
        highlighting: {
          magnetAdsorbed: {
            name: 'stroke',
            args: {
              attrs: {
                fill: '#fff',
                stroke: '#31d0c6',
                strokeWidth: 4
              }
            }
          }
        },
        connecting: {
          snap: true,
          allowBlank: false,
          allowLoop: false,
          highlight: true,
          //connector: 'algo-connector',
          connectionPoint: 'anchor',
          anchor: 'center',
          validateMagnet({ magnet }) {
            // return magnet.getAttribute('port-group') !== 'top'

            // 限制连线配置
            return true
          },
          createEdge() {
            return graph.createEdge({
              id: 'a' + uuid(),
              shape: 'dag-edge',
              router: {
                name: 'orth',
              },
              attrs: {
                line: {
                  strokeDasharray: '5 5',
                  targetMarker: {
                    name: 'block',
                    width: 12,
                    height: 8
                  }
                }
              },
              zIndex: -1
            })
          }
        },
        selecting: {
          enabled: true,
          multiple: true,
          rubberEdge: true,
          rubberNode: true,
          modifiers: 'shift',
          rubberband: true
        },
        keyboard: true,
        clipboard: true,
        history: true
      })
      this.graph = graph


      graph.on('edge:contextmenu', ({ e, x, y, edge, view }) => {
        console.log('event ', e, ' x ,y', x, y)
        this.showContextMenu = true
        this.$nextTick(() => {
          this.$refs.menuBar.initFn(e.offsetX, e.offsetY, { type: 'edge', item: edge })
        })
      })

      graph.on('node:contextmenu', ({ e, x, y, node, view }) => {
        this.showContextMenu = true
        this.$nextTick(() => {
          // this.$refs.menuBar.setItem({ type: 'node', item: node })
          //const p = graph.localToPage(x, y)
          const pos = this.graph.clientToGraph(e.clientX, e.clientY)
          this.$refs.menuBar.initFn(pos.x, pos.y, { type: 'node', item: node })
        })
      })
      graph.on('edge:mouseenter', ({ cell }) => {
        cell.addTools('vertices', 'onhover')
      })

      graph.on('edge:mouseleave', ({ cell }) => {
        if (cell.hasTools('onhover')) {
          cell.removeTools()
        }
      })
      // 控制连接桩显示/隐藏
      const showPorts = (ports, show) => {
        for (let i = 0, len = ports.length; i < len; i = i + 1) {
          ports[i].style.visibility = show ? 'visible' : 'hidden'
        }
      }
      graph.on('node:mouseenter', () => {
        const container = document.getElementById('draw-cot')
        //console.log('mouseenter ', container)
        const ports = container.querySelectorAll(
          '.x6-port-body',
        )
        showPorts(ports, true)
      })
      graph.on('node:mouseleave', () => {
        const container = document.getElementById('draw-cot')
        const ports = container.querySelectorAll(
          '.x6-port-body',
        )
        showPorts(ports, false)
      })

      graph.on('edge:connected', ({ edge }) => {
        const source = graph.getCellById(edge.source.cell)
        const target = graph.getCellById(edge.target.cell)
        let data = {
          bpmnType: 'sequenceFlow',
          id: edge.id
        }
        edge.setData(data);
        edge.attr({
          line: {
            strokeDasharray: ''
          }
        })
        //edge.setRouter('orth')
      })

      graph.on('node:change:data', ({ node }) => {
        const edges = graph.getIncomingEdges(node)
        const { status } = node.getData()
        edges?.forEach((edge) => {
          if (status === 'running') {
            edge.attr('line/strokeDasharray', 5)
            edge.attr('line/style/animation', 'running-line 30s infinite linear')
          } else {
            edge.attr('line/strokeDasharray', '')
            edge.attr('line/style/animation', '')
          }
        })
      })
      graph.on('node:added', ({ node }) => {
        console.log('------add node', node);
        let data = node.getData();
        let id = node.id;
        node.id = id
        node.setData({
          ...data,
          id: id
        })
      })
      this.$refs.stencil.initGraph(graph);
    },
    // 初始化节点/边
    init(data = []) {
      const cells = []
      data.cells.forEach((item) => {
        if (item.shape === 'dag-edge') {
          cells.push(this.graph.createEdge(item))
        } else {
          delete item.component
          cells.push(this.graph.createNode(item))
        }
      })
      this.graph.resetCells(cells)
    },
    zoomFn(num) {
      this.graph.zoom(num)
    },
    centerFn() {
      const num = 1 - this.graph.zoom()
      num > 1 ? this.graph.zoom(num * -1) : this.graph.zoom(num)
      this.graph.centerContent()
    },
    startFn() {
      this.timer && clearTimeout(this.timer)
      this.init(JSON.parse(this.graphData))
      // this.showNodeStatus(Object.assign([], nodeStatusList))
      this.graph.centerContent()
    },
    createMenuFn() {

    },
    keyBindFn() {
      // copy cut paste
      this.graph.bindKey(['meta+c', 'ctrl+c'], () => {
        const cells = this.graph.getSelectedCells()
        if (cells.length) {
          this.graph.copy(cells)
        }
        return false
      })
      this.graph.bindKey(['meta+x', 'ctrl+x'], () => {
        const cells = this.graph.getSelectedCells()
        if (cells.length) {
          this.graph.cut(cells)
        }
        return false
      })
      this.graph.bindKey(['meta+v', 'ctrl+v'], () => {
        if (!this.graph.isClipboardEmpty()) {
          const cells = this.graph.paste({ offset: 32 })
          this.graph.cleanSelection()
          this.graph.select(cells)
        }
        return false
      })

      // undo redo
      this.graph.bindKey(['meta+z', 'ctrl+z'], () => {
        if (this.graph.history.canUndo()) {
          this.graph.history.undo()
        }
        return false
      })
      // delete
      this.graph.bindKey('backspace', () => {
        const cells = this.graph.getSelectedCells()
        if (cells.length) {
          this.graph.removeCells(cells)
        }
      })
    },
    saveFn() {
      localStorage.setItem('x6Json', JSON.stringify(this.graph.toJSON({ diff: true })))
      // 把当前的流程图导出成json数据
      let cells = JSON.stringify(this.graph.toJSON({ diff: true }));
      console.log(" cells is ", cells)
      // 构造请求参数
      let process = {
        id: this.id, // 流程定义模型id
        graphData: cells // 流程定义模型图json数据
      }
      // 调用后台接口，进行流程模型的新增或修改。
      updateProcess(process).then(response => {
        this.msgSuccess("新增成功");
      })

    },
    loadFn() {
      this.timer && clearTimeout(this.timer)
      const x6Json = JSON.parse(localStorage.getItem('x6Json'))
      this.startFn(x6Json.cells)
    },
    lockFn() {
      this.isLock = !this.isLock
      if (this.isLock) {
        this.graph.enablePanning()
        this.graph.enableKeyboard()
      } else {
        this.graph.disablePanning()
        this.graph.disableKeyboard()
      }
    },
    contextMenuFn(type, node) {
      console.log('type is ', type)
      switch (type) {
        case 'remove':
          if (node.item.data.bpmnType == 'sequenceFlow') {
            this.graph.removeEdge(node.item.id)
          } else {
            this.graph.removeNode(node.item.id)
          }
          break;
        case "source":
          this.$refs.dialogMysql.visible = true
          this.$refs.dialogMysql.init(node)
          break;
        case "userTask":
          console.log("show userTask", this.showUserTask, " this ", this)
          this.$refs.sequenceFlow.visible = false
          this.$refs.userTask.visible = true
          this.$refs.userTask.init(node)
          break;
        case "sequenceFlow":
          console.log("sequenceFlow this", this)
          this.$refs.userTask.visible = false
          this.$refs.sequenceFlow.visible = true
          this.$refs.sequenceFlow.init(node)
          break;
      }

      this.showContextMenu = false
    },

    showDrawerFn() {
      //this.$refs.stencil.visible = !this.$refs.stencil.visible
    },
    addNode(option) {
      const p = this.graph.pageToLocal(option.x, option.y)
      this.graph.addNode(Object.assign({}, option, p))
    }
  }
}
</script>

<style>
body {
  box-sizing: border-box;
  margin: 0;
}

header {
  display: flex;
  justify-content: flex-end;
  width: 100%;
  height: 65px;
}

header i {
  margin: 8px;
  font-size: 30px;

}

.item {
  padding: 10px;
}

.item:active {
  background-color: #1890ff;
}

.my-selecting {
  border: 1px solid red;
  display: block;
  z-index: 0;
}

@keyframes running-line {
  to {
    stroke-dashoffset: -1000;
  }
}

@keyframes spin {
  from {
    transform: rotate(0deg);
  }

  to {
    transform: rotate(360deg);
  }
}

.rightWin {
  overflow-y: scroll;
  background-color: #1890ff;
}

.rightWin {
  position: absolute;
  top: 50px;
  right: 0;
  bottom: 0;
  background-color: #1890ff;
  padding: 0 12px;
  width: 300px;
}

.x6-node-selected .node {
  border-color: #1890ff;
  border-radius: 2px;
  box-shadow: 0 0 0 4px #d4e8fe;
}

.x6-node-selected .node.success {
  border-color: #52c41a;
  border-radius: 2px;
  box-shadow: 0 0 0 4px #ccecc0;
}

.x6-node-selected .node.failed {
  border-color: #ff4d4f;
  border-radius: 2px;
  box-shadow: 0 0 0 4px #fedcdc;
}

.x6-edge:hover path:nth-child(2) {
  stroke: #1890ff;
  stroke-width: 1px;
}

.x6-edge-selected path:nth-child(2) {
  stroke: #1890ff;
  stroke-width: 1.5px !important;
}

.section-cot {
  display: flex;
}

.section-cot #container {
  position: relative;
  flex: 1;
}

.section-cot #container #draw-cot {
  width: 100%;
  height: 100%;
}

.setTitle {
  margin: 10px;
}

::-webkit-scrollbar {
  width: 0;
  height: 0;
}

#coverCot {
  display: flex;
  border: 1px solid #dfe3e8;
}
</style>
